#include <iostream>
#include "./net/client/ClientNet.cpp"
#include "./net/client/UserModel.cpp"
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
using namespace std;

static void sig_usr(int);
static void sig_child(int);
static pid_t childPid;

static void printIndex();
static void handleRegister(NET::UserModel *user);
static void handleLogin(NET::UserModel *user);
static void handleAddFriend(NET::UserModel *user);
static void handleFriendList(NET::UserModel *user);
static void handleChat(NET::UserModel *user);

const int OPERATION_UP = -1;// 返回上一级
const int OPERATION_EXIT = -2;// 退出

void* childThreadHandle(void* arg);

// 如果jsoncpp在/usr/local/lib时：usage: g++ main_client.cpp -ljsoncpp_static -o client_xiaoo
// 编译时，默认会去/usr/lib 和/usr/local/lib里面查找库文件，如果在其他目录需要特别指定，指定方法两中
// 指定方法1、使用相对目录指定库文件全名
// g++ main_client.cpp common/config.cpp ../lib/libjsoncpp_static.a -o client_xiaoo  -std=c++11 -lpthread
// 指定方法2、使用-L参数指定目录，-l(L的小写) 指定库文件名称(非全名,linux库文件命名为libxxx.a或者libxxx.so，xxx为名称)
// g++ main_client.cpp common/config.cpp -L../lib -ljsoncpp_static -o client_xiaoo  -std=c++11 -lpthread
int main(){
    // user对象包含玩家的连接状态，因此该为单例
    NET::UserModel *user = new NET::UserModel();
    user->isDebug = false;
    // user.registe("xiong.zhang", "123456");
    // user.login("大空翼", "123456");
    // user.addFriend(1, 2);
    // user.friendList(4);
    // user.chat(2, 1, "你好呀，萨瓦迪卡");
// @todo
// 暂无
    //注册信号
    signal(SIGCHLD, sig_child);
    signal(SIGINT, sig_usr);
    signal(SIGTERM, sig_usr);

    // 采用线程
    pthread_t thid;
    int re = pthread_create(&thid, NULL, childThreadHandle, (void*)user);
    if (re != 0)
    {
        printf("创建线程失败\n");
        exit(0);
    }
    
    // 主线程
    printIndex();

    while(1){
        if(user->isDebug){
            printf("当前uid:%d\n", user->uid);
        }
        
        char operation[4096];
        gets(operation);
        if(user->isDebug){
            printf("******* input *******\n");
            printf("%s\n", operation);
            printf("%d\n", atoi(operation));
            printf("******* input over **\n");
        }
        
        switch(atoi(operation)){
            case 1:
                handleRegister(user);
                break;
            case 2:
                handleLogin(user);
                break;
            case 3:
                handleAddFriend(user);
                break;
            case 4:
                handleFriendList(user);
                break;
            case 5:
                handleChat(user);
                break;
            case OPERATION_EXIT:
                delete(user);
                printf("已退出\n");
                exit(0);
                break;
            default:
                break;
        }
    }

    delete(user);
    return 0;
}

void* childThreadHandle(void* arg){
    NET::UserModel* user = (NET::UserModel*)arg;
    while (1)
    {
        if(user->isDebug){
            printf("子线程等待接收数据...\n");
        }

        std::string route;
        std::string rawJson = user->onRecvMsg(&route);

        if(route == "user/chatCallback" || route == "user/recvChatCallback"){
            
        }else{
            printIndex();
        }
        
    }
}

static void printIndex(){
    // 等待1秒，先让子线程的请求回调执行屏幕输出，再打印操作
    // sleep(1);
    printf("\n\n请输入操作...\n"
        "1、注册\n"
        "2、登陆\n"
        "3、添加好友\n"
        "4、好友列表\n"
        "5、聊天\n"
        "-1、返回上一级\n"
        "-2、退出\n");
}

static void handleRegister(NET::UserModel *user){
    printf("请输入需要注册的账号和密码，账号和密码空格分开\n");
    char data[100];
    gets(data);
    std::string userAndPw = data;
    std::string subStr = " ";
    
    int pos = userAndPw.find(subStr, 0);
    if(pos <= 0){
        printf("账号密码格式错误\n");
        printIndex();
        return;
    }
    std::string username = userAndPw.substr(0, pos);
    std::string pw = userAndPw.substr(pos + 1, userAndPw.length());

    NET::clientReturnStruct re = user->registe(username, pw);
    if(re.code != NET::CLIENT_ERROR_SUCCESS){
        printf("请求失败 with msg:%s\n", re.msg.c_str());
    }
}

static void handleLogin(NET::UserModel *user){
    printf("请输入UID和密码，UID和密码空格分开\n");
    char data[100];
    gets(data);
    std::string userAndPw = data;
    std::string subStr = " ";
    
    int pos = userAndPw.find(subStr, 0);
    if(pos <= 0){
        printf("UID和密码格式错误\n");
        printIndex();
        return;
    }
    int uid = atoi(userAndPw.substr(0, pos).c_str());
    std::string pw = userAndPw.substr(pos + 1, userAndPw.length());

    NET::clientReturnStruct re = user->login(uid, pw);
    if(re.code != NET::CLIENT_ERROR_SUCCESS){
        printf("请求失败 with msg:%s\n", re.msg.c_str());
    }
}

static void handleAddFriend(NET::UserModel *user){
    printf("请输入被添加的好友uid\n");
    char data[100];
    gets(data);
    int beuid = atoi(data);
    if(beuid <= 0){
        printf("UID格式错误\n");
        printIndex();
        return;
    }
    NET::clientReturnStruct re = user->addFriend(beuid);
    if(re.code != NET::CLIENT_ERROR_SUCCESS){
        printf("请求失败 with msg:%s\n", re.msg.c_str());
    }
}

static void handleFriendList(NET::UserModel *user){
    NET::clientReturnStruct re = user->friendList();
    if(re.code != NET::CLIENT_ERROR_SUCCESS){
        printf("请求失败 with msg:%s\n", re.msg.c_str());
    }
}

static void handleChat(NET::UserModel *user){
    printf("请输入要聊天的好友uid:\n");
    char data[100];
    gets(data);
    int uid = atoi(data);
    if(uid <= 0){
        printf("UID格式错误\n");
        printIndex();
        return;
    }
    printf("请输入聊天内容(-1返回上一级):\n");

    while (1)
    {
        char content[100];
        gets(content);
        std::string msg = content;
        int operation_msg = atoi(content);
        if(operation_msg == OPERATION_UP){
            printf("已退出当前聊天会话.");
            printIndex();
            return;
        }

        NET::clientReturnStruct re = user->chat(uid, msg);
        if(re.code != NET::CLIENT_ERROR_SUCCESS){
            printf("请求失败 with msg:%s\n", re.msg.c_str());
        }
    }
    
}



static void sig_usr(int signo){
    if(signo == SIGTERM || signo == SIGINT){
        kill(childPid, SIGABRT);
        printf("killed child \n");
    }else{
        printf("catch other signo %d\n", signo);
    }
}

static void sig_child(int signo){
    printf("sign handle SIGCHLD\n");
    wait(NULL);
    exit(0);
}